-- | Config.hs
-- A module for reading and writing program's config file.

module Config where

import Buffers

import Data.ConfigFile
import qualified Data.HashTable as H

import Data.List
import Control.OldException
import Control.Monad.Error

-- | Config HashTable.
type Config = H.HashTable OptionSpec String

-- | Default config. If no value in config file,
-- then default value will be used.
defaultConfig = [ ("client", "matsuri")
                , ("version", "unknown")
                , ("OS", "unknown")
                , ("show_offline", "yes")
                , ("buffer_width", "20")
                , ("system_color", "(white,def)")
                , ("buffer_color", "(white,def)")
                , ("buffer_hicolor", "(blue,cyan)")
                , ("info_color", "(white,blue)")
                , ("msg_color", "(white,black)")
                , ("mymsg_color", "(yellow,def)")
                , ("history_color", "(cyan,def)")
                , ("status_icolor", "(white,magenta)")
                , ("status_ecolor", "(yellow,red)")
                , ("unknown_color", "(magenta,def)")
                ]

-- | Reading "main" and "account_*" sections of config file.
readConfig :: FilePath -> IO (Config, Accounts)
readConfig filePath = do
  config <- H.fromList H.hashString defaultConfig
  accounts <- H.new (==) H.hashString
  fileCnt <- handle (\_ -> return "[main]") (readFile filePath)
  confs <- runErrorT $ do
      conf <- readstring emptyCP fileCnt

      -- reading and updating main options
      mainOptions <- items conf "main"
      liftIO $ mapM_ (updateValue config) mainOptions

      -- reading account_* sections
      let sects = filter (\acc -> take 8 acc == "account_") (sections conf)
      accountsList <- forM sects $ \account -> do
                             accountItems <- items conf account
                             return (drop 8 account, accountItems)
      liftIO $ mapM_ (updateAccount accounts) accountsList

      return (config, accounts)
  case confs of
    Left _ -> error "can't parse config file"
    Right conf -> return conf

-- | Update option's value or exit with error if no such option.
updateValue :: Config -> (OptionSpec, String) -> IO ()
updateValue config (option, value) = do
    maybeValue <- H.lookup config option
    case maybeValue of
        Just _ -> do H.update config option value
                     return ()
        Nothing -> error $ "unknown \"" ++ option ++ "\" option"

-- | Update account in HashTable or exit with error if wrong options.
updateAccount :: Accounts -> (String, [(OptionSpec, String)]) -> IO ()
updateAccount accounts (accountName, accountItems) = do
    users' <- H.new (==) H.hashString
    H.update accounts accountName
      Account { username = findItem "username"
              , server = findItem "server"
              , password = findItem "password"
              , resource = findItem "resource"
              , priority = read $ findItem "priority"
              , defaultNick = findItem "nick"
              , connection = Nothing
              , users = users'
              , accountContents = []
              }
    return ()
    where findItem itemName =
           case find (\(name, _) -> name == itemName) accountItems of
             Just value -> snd value
             _ -> error $ "bad \"" ++ accountName ++ "\" account options"

-- | Return value from config or "".
getConfigValue :: String -> [(String, String)] -> String
getConfigValue name =
    snd . maybe ("","") id . find (\(name',value) -> name == name')

-- | Put value to config.
putConfigValue :: Config -> String -> (String, String) -> IO (StatusResult a)
putConfigValue config value (name, _) = do
    H.update config name value
    return $ StatusResult $ Right (undefined, "set "++name++"="++value)

isShowOffline configList = (getConfigValue "show_offline" configList) == "yes"
